#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/proc_fs.h>
#include <linux/list.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/list_sort.h>
#include <linux/uaccess.h>

#define MAX_LENGTH	256

#define MIN_LEN(_a, _b)					\
	({ int a_len = strlen(_a);				\
	   int b_len = strlen(_b);				\
	   (a_len < b_len) ? a_len : b_len; })

#define IGNORE_CASE(_ch) (((_ch > 0x40) && (_ch < 0x5B)) ? _ch + 0x20 : _ch)

#define COMMAND_WORD(my) ((my_buffer_size == sizeof(my)) && (strncmp(my_buffer, my, my_buffer_size - 1) == 0))

//--------------Working with list---------------------------------

static struct list_head snapshot;	/* Head of the list */

struct process_list {
	int pid;
	char comm[MAX_LENGTH];
	struct list_head list;
};
// Fill/clean list
static int get_snapshot(void)
{
	struct task_struct *task = NULL;
	struct process_list *ps = NULL;

	INIT_LIST_HEAD(&snapshot);

	for_each_process(task) {
		ps = kmalloc(sizeof(struct process_list), GFP_KERNEL);
		if (!ps) {
			printk("get_snapshot with error in for_each_process\n");
			return -ENOMEM;  //Cannot allocate memory
		}
		ps->pid = task->pid;
		memcpy(ps->comm, task->comm, sizeof(task->comm));
		list_add_tail(&(ps->list), &snapshot);
	}

	printk("get_snapshot\n");
	return 0;
}

static int clean_snapshot(void)
{
	struct process_list *ps_list = NULL;
	struct list_head *ps = NULL;
	struct list_head *ps_safe = NULL;

	list_for_each_safe(ps, ps_safe, &snapshot) {
		ps_list = list_entry(ps, struct process_list, list);
		list_del(ps);
		kfree(ps_list);
	}

	printk("clean_snapshot\n");
	return 0;
}

// Sort methods
static int sort_by_pid(void *priv, struct list_head *a, struct list_head *b)
{
	struct process_list *ps_a = NULL;
	struct process_list *ps_b = NULL;

	ps_a = list_entry(a, struct process_list, list);
	ps_b = list_entry(b, struct process_list, list);

	return ps_a->pid - ps_b->pid;
}

static int sort_by_pid_backward(void *priv, struct list_head *a, struct list_head *b)
{
	struct process_list *ps_a = NULL;
	struct process_list *ps_b = NULL;

	ps_a = list_entry(a, struct process_list, list);
	ps_b = list_entry(b, struct process_list, list);

	return ps_b->pid - ps_a->pid;
}

static int sort_abc(void *priv, struct list_head *a, struct list_head *b)
{
	struct process_list *ps_a = NULL;
	struct process_list *ps_b = NULL;
	int length = 0;
	int i = 0;
	int res = 0;

	ps_a = list_entry(a, struct process_list, list);
	ps_b = list_entry(b, struct process_list, list);
	length = MIN_LEN(ps_a->comm, ps_b->comm);
	for (i = 0; i < length; i++) {
		res = IGNORE_CASE(ps_a->comm[i]) - IGNORE_CASE(ps_b->comm[i]);
		if (res != 0)
			break;
	}

	return res;
}

static int sort_zyx(void *priv, struct list_head *a, struct list_head *b)
{
	struct process_list *ps_a = NULL;
	struct process_list *ps_b = NULL;
	int length = 0;
	int i = 0;
	int res = 0;

	ps_a = list_entry(a, struct process_list, list);
	ps_b = list_entry(b, struct process_list, list);
	length = MIN_LEN(ps_a->comm, ps_b->comm);
	for (i = 0; i < length; i++) {
		res = IGNORE_CASE(ps_b->comm[i]) - IGNORE_CASE(ps_a->comm[i]);
		if (res != 0)
			break;
	}

	return res;
}

//--------------Working with /proc---------------------------------

static struct proc_dir_entry *proc;

//--------------Working with seq_file------------------------------

static void *proc_seq_start(struct seq_file *sf, loff_t *pos)
{
	printk("snapshot_seq_start\n");
	if (!sf->private)
		sf->private = seq_list_start(&snapshot, *pos);
	else if (sf->private == &snapshot)
		return NULL;	
	return sf->private;
}

static void *proc_seq_next(struct seq_file *sf, void *v, loff_t *pos)
{
	(*pos)++;
	sf->private = ((struct list_head *)sf->private)->next;
        printk("snapshot_seq_next\n");
	return NULL;
}

static void proc_seq_stop(struct seq_file *sf, void *v) {	}

static int proc_seq_show(struct seq_file *sf, void *v)
{
	int err = 0;
	struct process_list *ps = NULL;

	ps = list_entry((struct list_head *)v, struct process_list, list);
	err = seq_printf(sf, "<%d>: %s\n", ps->pid, ps->comm);

	printk("snapshot_seq_show\n");
	return err;
}

static const struct seq_operations snapshot_ops = {
	.start		= proc_seq_start,
	.next		= proc_seq_next,
	.stop		= proc_seq_stop,
	.show		= proc_seq_show,
};

static int snapshot_open(struct inode *inode, struct file *file)
{
	return seq_open(file, &snapshot_ops);
}

static ssize_t snapshot_write(struct file *file, const char __user *buffer, unsigned long count, loff_t *data)
{
	char my_buffer[MAX_LENGTH];
	unsigned long my_buffer_size = count;
	int err = 0;

	memset(my_buffer, '\0', MAX_LENGTH);

	/* Read user space */
	if (my_buffer_size > MAX_LENGTH)
		my_buffer_size = MAX_LENGTH;
	if (copy_from_user(my_buffer, buffer, my_buffer_size)) {
		printk("snapshot_write with error copy_from_user\n");
		return -EFAULT;
	}
	printk("Snapshot was written\n");
	printk("Snapshot was written: %s\n", my_buffer);
	/* Parse command */
	if (COMMAND_WORD("refresh")) {
		printk("Refresh command\n");
		clean_snapshot();
		err = get_snapshot();
	} else if (COMMAND_WORD("straight")) {
		printk("Straight command\n");
		clean_snapshot();
		err = get_snapshot();
		list_sort(NULL, &snapshot, sort_by_pid);
	} else if (COMMAND_WORD("reverse")) {
		printk("Reverse command\n");
		clean_snapshot();
		err = get_snapshot();
		list_sort(NULL, &snapshot, sort_by_pid_backward);
	} else if (COMMAND_WORD("abc")) {
		printk("abc command\n");
		clean_snapshot();
		err = get_snapshot();
		list_sort(NULL, &snapshot, sort_abc);
	} else if (COMMAND_WORD("zyx")) {
		printk("zyx command\n");
		clean_snapshot();
		err = get_snapshot();
		list_sort(NULL, &snapshot, sort_zyx);
	} else {
		printk("Undefined command\n");
	}

	if (err < 0) {
		printk("snapshot_write with error %d\n", err);
		return err;
	}

	printk("snapshot_write\n");
	return my_buffer_size;
}

static const struct file_operations proc_fops = {
	.owner		= THIS_MODULE,
	.open		= snapshot_open,
	.read		= seq_read,
	.write		= snapshot_write
};

//--------------Module init/exit------------------------------------

static int __init init_snapshot(void)
{
	int err = 0;
	printk("snapshot_init\n");

	proc = proc_create("snapshot", 0666, NULL, &proc_fops);
	if (!proc) {
		printk("snapshot_init with error in proc_create\n");
		return -EFAULT;
	}

//	INIT_LIST_HEAD(&snapshot);
	err = get_snapshot();
	if (err != 0) {
		printk("snapshot_init with error %d\n", err);
		return err;
	}

	printk("snapshot_init\n");
	return 0;
}

static void __exit exit_snapshot(void)
{
	clean_snapshot();
	remove_proc_entry("snapshot", NULL);

	printk("snapshot_exit\n");
}

module_init(init_snapshot)
module_exit(exit_snapshot)

//--------------Information about module----------------------------

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Anastasiya Solomina <sterlyadka333@mail.ru>");
